using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

using EntModApp.EntServices;
using EntModApp.EntServices.Data;

namespace DemoApp.DataAccess 
{
  public partial class GroupQueryableRepository : 
    DbContextQueryableRepository<DemoAppModelDbContext, User, Group>,
    IQueryableGroupRepository
  {
    public GroupQueryableRepository(DemoAppModelDbContext context)
      : this(context, false)
    {
    }

    public GroupQueryableRepository()
      : this(new DemoAppModelDbContext(), true)
    {
    }

    protected GroupQueryableRepository(DemoAppModelDbContext context, bool ownContext)
      : base(context, context.Users, ownContext)
    {
    }
  }

  public partial class GroupDataRepository : 
    DbContextDataRepository<DemoAppModelDbContext, User, Group, GroupLoadSettings>,
    IGroupRepository
  {
    public GroupDataRepository(DemoAppModelDbContext context)
      : this(context, false)
    {
    }

    public GroupDataRepository()
      : this(new DemoAppModelDbContext(), true)
    {
    }

    protected GroupDataRepository(DemoAppModelDbContext context, bool ownContext)
      : base(context, context.Users, ownContext)
    {
    }

    public override IEnumerable<Group> RetrieveAll(out int total, bool retrieveTotal)
    {
      var query = new GroupLoadSettings
      {
        Paging = new PagingSettings { ComputeTotal = retrieveTotal }
      };
      return Retrieve(query, out total);
    }

    public override IEnumerable<Group> Retrieve(GroupLoadSettings query, out int total)
    {
      var dbQuery = DataSet.OfType<Group>().ApplyGroupLoadSettings(query, out total);
      return dbQuery.ToList();
    }

    public override int Count()
    {
      return DataSet.Count();
    }

    public override int Count(GroupLoadSettings query)
    {
      var dbQuery = DataSet.OfType<Group>().ApplyGroupFilterExpression(query.Filter);
      return dbQuery.Count();
    }
  }

  public static class GroupQueryableRepositoryExtensions
  {
    public static IQueryable<Group> ApplyGroupLoadSettings(this IQueryable<Group> query, GroupLoadSettings loadSettings, out int total)
    {
      if (loadSettings == null)
        loadSettings = new GroupLoadSettings();

      query = query.ApplyGroupInclusionSettings(loadSettings.Inclusion);

      query = query.ApplyGroupFilterExpression(loadSettings.Filter);

      query = query.ApplyGroupSortingSettings(loadSettings.Sorting);

      query = query.ApplyPagingSettings(loadSettings.Paging, out total);

      query = query.ApplyGroupSelectionSettings(loadSettings.Selection);

      return query;
    }

    public static IEnumerable<string> GetDirectGroupInclusionPaths(this GroupInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetDirectUserInclusionPaths())
        yield return path;

      if (inclusionSettings.IncludeUsers) yield return "Users";

      yield break;
    }

    public static IEnumerable<string> GetGroupInclusionPaths(this GroupInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetDirectGroupInclusionPaths())
        yield return path;

      if (inclusionSettings.IncludeUsers)
        foreach (var referencePath in inclusionSettings.IncludedUsersSettings.Inclusion.GetUserInclusionPaths())
          yield return "Users." + referencePath;

      yield break;
    }

    public static IQueryable<Group> ApplyGroupInclusionSettings(this IQueryable<Group> query, GroupInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetGroupInclusionPaths())
        query = query.Include(path);

      return query;
    }

    public static IQueryable<Group> ApplyGroupFilterExpression(this IQueryable<Group> query, GroupFilterExpression filterExpression)
    {
      if (filterExpression.ByKey != null)
        query = query.Where(o => o.UserId == filterExpression.ByKey.UserId);

      var parameter = Expression.Parameter(typeof(Group), "e");
      if (filterExpression.UserIdFilter != null)
        query = query.Where(filterExpression.UserIdFilter.CreateExpressionBody<Group, Guid>(GroupPropertyNames.UserId, parameter).ToLambda<Group>(parameter));
      if (filterExpression.UserNameFilter != null)
        query = query.Where(filterExpression.UserNameFilter.CreateExpressionBody<Group, string>(GroupPropertyNames.UserName, parameter).ToLambda<Group>(parameter));
      if (filterExpression.PasswordFilter != null)
        query = query.Where(filterExpression.PasswordFilter.CreateExpressionBody<Group, string>(GroupPropertyNames.Password, parameter).ToLambda<Group>(parameter));

      if (filterExpression.Skip.HasValue)
        query = query.Skip(filterExpression.Skip.Value);
      if (filterExpression.Take.HasValue)
        query = query.Take(filterExpression.Take.Value);

      return query;
    }

    public static IQueryable<Group> ApplyGroupSortingSettings(this IQueryable<Group> query, GroupSortingSettings sortingSettings)
    {
      return query;
    }

    public static IQueryable<Group> ApplyGroupSelectionSettings(this IQueryable<Group> query, GroupSelectionSettings selectionSettings)
    {
      return query;
    }

  }
}
